---
title: "Suspend & Resume | Workflows"
description: "Suspend and resume in Mastra workflows allows you to pause execution while waiting for external input or resources."
---

# Suspend & Resume

Workflows can be paused at any step to collect additional data, wait for API callbacks, throttle costly operations, or request [human-in-the-loop](/docs/v1/workflows/human-in-the-loop) input. When a workflow is suspended, its current execution state is saved as a snapshot. You can later resume the workflow from a [specific step ID](/docs/v1/workflows/snapshots#retrieving-snapshots), restoring the exact state captured in that snapshot. [Snapshots](/docs/v1/workflows/snapshots) are stored in your configured storage provider and across deployments and application restarts.
## Pausing a workflow with `suspend()`

Use `suspend()` to pause workflow execution at a specific step. You can define a suspend condition in the step’s `execute` block using values from `resumeData`.

- If the condition isn’t met, the workflow pauses and returns `suspend()`.
- If the condition is met, the workflow continues with the remaining logic in the step.

![Pausing a workflow with suspend()](/img/workflows/workflows-suspend.jpg)

```typescript {9-11,16-18} title="src/mastra/workflows/test-workflow.ts" showLineNumbers copy
const step1 = createStep({
  id: "step-1",
  inputSchema: z.object({
    userEmail: z.string()
  }),
  outputSchema: z.object({
    output: z.string()
  }),
  resumeSchema: z.object({
    approved: z.boolean()
  }),
  execute: async ({ inputData, resumeData, suspend }) => {
    const { userEmail } = inputData;
    const { approved } = resumeData ?? {};

    if (!approved) {
      return await suspend({});
    }

    return {
      output: `Email sent to ${userEmail}`
    };
  }
});

export const testWorkflow = createWorkflow({
  id: "test-workflow",
  inputSchema: z.object({
    userEmail: z.string()
  }),
  outputSchema: z.object({
    output: z.string()
  })
})
  .then(step1)
  .commit();
```

## Restarting a workflow with `resume()`

Use `resume()` to restart a suspended workflow from the step where it paused. Pass `resumeData` matching the step's `resumeSchema` to satisfy the suspend condition and continue execution.

![Restarting a workflow with resume()](/img/workflows/workflows-resume.jpg)

```typescript showLineNumbers copy
import { step1 } from "./workflows/test-workflow";

const workflow = mastra.getWorkflow("testWorkflow");
const run = await workflow.createRun();

await run.start({
  inputData: {
    userEmail: "alex@example.com"
  }
});

const handleResume = async () => {
  const result = await run.resume({
    step: step1,
    resumeData: { approved: true }
  });
};
```

Passing the `step` object provides full type-safety for `resumeData`. Alternatively, you can pass a step ID for more flexibility when the ID comes from user input or a database.

```typescript showLineNumbers copy
const result = await run.resume({
  step: "step-1",
  resumeData: { approved: true }
});
```

If only one step is suspended, you can omit the step argument entirely and Mastra will resume the last suspended step in the workflow.

When resuming with only a `runId`, create a run instance first using `createRunAsync()`.

```typescript showLineNumbers copy
const workflow = mastra.getWorkflow("testWorkflow");
const run = await workflow.createRunAsync({ runId: "123" });

const stream = run.resume({
  resumeData: { approved: true }
});
```

You can call `resume()` from anywhere in your application, including HTTP endpoints, event handlers, in response to [human input](/docs/v1/workflows/human-in-the-loop), or timers.

```typescript showLineNumbers copy
const midnight = new Date();
midnight.setUTCHours(24, 0, 0, 0);

setTimeout(async () => {
  await run.resume({
    step: "step-1",
    resumeData: { approved: true }
  });
}, midnight.getTime() - Date.now());
```

## Accessing suspend data with `suspendData`
When a step is suspended, you may want to access the data that was provided to `suspend()` when the step is later resumed. Use the `suspendData` parameter in your step's execute function to access this data.

```typescript {22-25,29-30} title="src/mastra/workflows/user-approval.ts" showLineNumbers copy
const approvalStep = createStep({
  id: "user-approval",
  inputSchema: z.object({
    requestId: z.string()
  }),
  resumeSchema: z.object({
    approved: z.boolean()
  }),
  suspendSchema: z.object({
    reason: z.string(),
    requestDetails: z.string()
  }),
  outputSchema: z.object({
    result: z.string()
  }),
  execute: async ({ inputData, resumeData, suspend, suspendData }) => {
    const { requestId } = inputData;
    const { approved } = resumeData ?? {};
    
    // On first execution, suspend with context
    if (!approved) {
      return await suspend({
        reason: "User approval required",
        requestDetails: `Request ${requestId} pending review`
      });
    }
    
    // On resume, access the original suspend data
    const suspendReason = suspendData?.reason || "Unknown";
    const details = suspendData?.requestDetails || "No details";
    
    return {
      result: `${details} - ${suspendReason} - Decision: ${approved ? 'Approved' : 'Rejected'}`
    };
  }
});
```

The `suspendData` parameter is automatically populated when a step is resumed and contains the exact data that was passed to the `suspend()` function during the original suspension. This allows you to maintain context about why the workflow was suspended and use that information during the resume process.

## Identifying suspended executions

When a workflow is suspended, it restarts from the step where it paused. You can check the workflow's `status` to confirm it's suspended, and use `suspended` to identify the paused step or [nested workflow](/docs/v1/workflows/overview#workflows-as-steps).

```typescript {11} showLineNumbers copy
const workflow = mastra.getWorkflow("testWorkflow");
const run = await workflow.createRun();

const result = await run.start({
  inputData: {
    userEmail: "alex@example.com"
  }
});

if (result.status === "suspended") {
  console.log(result.suspended[0]);
  await run.resume({
    step: result.suspended[0],
    resumeData: { approved: true }
  });
}
```

**Example output**

The `suspended` array contains the IDs of any suspended workflows and steps from the run. These can be passed to the `step` parameter when calling `resume()` to target and resume the suspended execution path.

```typescript
[ 'nested-workflow', 'step-1' ]
```

## Sleep & Events

Sleep and event methods can be used to pause execution at the workflow level, which sets the status to `waiting`. By comparison, `suspend()` pauses execution within a specific step and sets the status to `suspended`.

**Available methods:**

- [`.sleep()`](/reference/v1/workflows/workflow-methods/sleep): Pause for a specified number of milliseconds
- [`.sleepUntil()`](/reference/v1/workflows/workflow-methods/sleepUntil) : Pause until a specific date
- [`.waitForEvent()`](/reference/v1/workflows/workflow-methods/waitForEvent): Pause until an external event is received
- [`.sendEvent()`](/reference/v1/workflows/workflow-methods/sendEvent) : Send an event to resume a waiting workflow

## Related

- [Control Flow](/docs/v1/workflows/control-flow)
- [Human-in-the-loop](/docs/v1/workflows/human-in-the-loop)
- [Snapshots](/docs/v1/workflows/snapshots)
- [Time Travel](/docs/v1/workflows/time-travel)
